package oop.ex3.sockets;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;

import oop.ex3.messages.exception.*;

import oop.ex3.messages.*;

public class ClientSocket{
	
	private final static String END = "END";
	
	//TODO 5000? really?
	private static final int TIMEOUT = 5000;
	
	private Socket _socket;
	private InetSocketAddress _otherPeer;
	protected DataInputStream _myDataIStream;
	protected DataOutputStream _myDataOStream;
	
	public ClientSocket(Socket socket) throws IOException{
		_socket = socket;
		_otherPeer = new InetSocketAddress(socket.getInetAddress(), 
				socket.getPort());
		_myDataOStream = new DataOutputStream(_socket.getOutputStream());
		_myDataIStream = new DataInputStream(_socket.getInputStream());
	}
			
	public ClientSocket(InetSocketAddress dest) 
	throws IOException, UnknownHostException{
	 	_socket = new Socket(dest.getAddress(), dest.getPort());
//	 	this.setSoTimeout(TIMEOUT);
		_otherPeer = new InetSocketAddress(dest.getAddress(), dest.getPort());
		_myDataOStream = new DataOutputStream(_socket.getOutputStream());
		_myDataIStream = new DataInputStream(_socket.getInputStream());
	}
	
//	public ClientSocket() throws IOException{
////		s.setSoTimeout(TIMEOUT);
//		//TODO like this? for sure?
//		_otherPeer = new InetSocketAddress(s.getInetAddress(), s.getPort());
//		_myDataOStream = new DataOutputStream(_socket.getOutputStream());
//		_myDataIStream = new DataInputStream(_socket.getInputStream());
//	}
	
	public InetSocketAddress getOtherPeer(){
		return _otherPeer;
	}
	
	public void writeToSocket(String msg) throws IOException{
		_myDataOStream.writeUTF(msg);
	}
	
	public void writeToSocket(int port) throws IOException{
		_myDataOStream.writeInt(port);
	}
	
	public void writeToSocket(long fileContent) throws IOException{
		_myDataOStream.writeLong(fileContent);
	}

	public void SendMessage(MessageInterface msg) 
	throws InvalidHeaderException, IOException{
		msg.sendMessage(this);
		writeToSocket(END);
	}
		
//	public void SendMessage(SimpleMessage msg) throws IOException{
//		System.out.println("OUT <to: "+_socket.getPort()+"> "+msg.getHeader());
//		writeToSocket(msg.getHeader());
//		writeToSocket(END);
//	}
	
//	public void sendMessage(PeerInfoMessage msg) throws IOException{
//		System.out.println("OUT <to: "+_socket.getPort()+"> "+msg.getHeader());
//		writeToSocket(msg.getHeader());
//		writeToSocket(msg.getIP());
//		writeToSocket(msg.getPort());
//		writeToSocket(END);
//	}
	
//	public void sendMessage(FileMessage msg) throws IOException{
//		System.out.println("OUT <to: "+_socket.getPort()+"> "+msg.getHeader());
//		writeToSocket(msg.getHeader());
//		writeToSocket(msg.getFileName());
//		writeToSocket(END);
//	}
	
//	public void sendMessage(FileContentMessage msg) throws IOException{
//		System.out.println("OUT <to: "+_socket.getPort()+"> "+msg.getHeader());
//		writeToSocket(msg.getHeader());
//		writeToSocket(msg.getFile().length());
//		FileInputStream is = new FileInputStream(msg.getFile());
//		int result;
//		while ((result = is.read()) != -1){
//			writeToSocket(result);
//		}
//		is.close();
//		writeToSocket(END);
//	}
	
	public MessageInterface readMessage() 
	throws IOException, UnexpectedMessageEndException{
		//TODO this thrown IPException!!
		String header = _myDataIStream.readUTF();
		try {
			String type = ValidHeaders.getMsgType(header);
			if (type.equals(ValidHeaders.SIMPLE_MESSAGE)){
//				System.out.println("its a simple message!");
				return new SimpleMessage(header).readMessage(this);
			}
			else if (type.equals(ValidHeaders.FILE_MESSAGE)){
				return new FileMessage(header).readMessage(this);
			}
			else if (type.equals(ValidHeaders.PEER_INFO)){
				return new PeerInfoMessage(header).readMessage(this);
			}
			else {
				return new FileContentMessage(header).readMessage(this);
			}
		} catch (InvalidHeaderException e) {
			// TODO doSomething
		}
		//TODO!!!!!
		return null;
		
	}
	
//	public FileMessage readFileMessage(String header) throws IOException{
//		System.out.println("IN <from: "+_socket.getPort()+"> "+header);
//		String file = _myDataIStream.readUTF();
//		_myDataIStream.readUTF();
//		return new FileMessage(header, file);
//	}
	
//	public PeerInfoMessage readPeerInfoMessage(String header) 
//	throws IOException{
//		System.out.println("IN <from: "+_socket.getPort()+"> "+header);
//		String ip = _myDataIStream.readUTF();
//		//TODO
////		System.out.println("ip "+ip);
//		int port = _myDataIStream.readInt();
////		System.out.println("port "+port);
//		//read end
//		_myDataIStream.readUTF();
//		return new PeerInfoMessage(header, new InetSocketAddress(ip,port));
//	}
	
//	public SimpleMessage readSimpleMessage(String header) 
//	throws UnexpectedMessageEndException, IOException{		
//		System.out.println("IN <from: "+_socket.getPort()+"> "+header);
//		if (!_myDataIStream.readUTF().equals(END)){
////			System.out.println("not end, what can i do?");
//			throw new UnexpectedMessageEndException();
//		}
////		System.out.println("i read the simple message");
//		return new SimpleMessage(header);
//	}
	
	//TODO write this method
//	public FileContentMessage readFileContent(String header){
//		
//	}
	
	public void closeClientSocket(){
		
		try {
			_myDataIStream.close();
		} catch (IOException e) {

		}
		try {
			_myDataOStream.close();
		} catch (IOException e) {

		}
		try {
			_socket.close();
		} catch (IOException e) {

		}
	}

	public String readUTF() throws IOException {
		return _myDataIStream.readUTF();
	}

	public int readInt() throws IOException {
		return _myDataIStream.readInt();
	}

	public long readLong() throws IOException {
		return _myDataIStream.readLong();
		
	}

	public int getPort() {
		return _socket.getPort();
	}

	public void write(int byteInt) throws IOException {
		_myDataOStream.write(byteInt);
		
	}
	
}
